package cn.xzqwjw.taskmanager.utils;

import cn.xzqwjw.taskmanager.common.customEnum.ResponseCodeEnum;
import cn.xzqwjw.taskmanager.common.exception.CustomException;
import cn.xzqwjw.taskmanager.domain.vo.CustomOptionVo;
import cn.xzqwjw.taskmanager.domain.vo.UploadConfigVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

/**
 * 实现文件相关功能
 * mkdirs()可以建立多级文件夹，mkdir()只会建立一级的文件夹
 * new File("/tmp/one/two/three").mkdirs()会建立tmp/one/two/three四级目录
 * new File("/tmp/one/two/three").mkdir()不会建立任何目录
 * 因为找不到/tmp/one/two目录，结果返回false
 *
 * @author rush
 */
@Slf4j
public class FileUtil {

  /**
   * 定义操作系统设置的目录分隔符
   */
  private final static String SEPARATOR = File.separator;

  /**
   * 从配置文件得到上传文件存放的根目录，最后以“/”结尾
   *
   * @return 根目录路径字符串
   */
  public static String getPhysicsRootDir() {
    return gainUlDlPhysicsRootPath(CustomOptionVo.getUlRootDir());
  }

  /**
   * 从配置文件得到上传文件临时存放的根目录，最后以“/”结尾
   *
   * @return 临时存放根目录路径字符串
   */
  public static String getPhysicsRootDirTemp() {
    return gainUlDlPhysicsRootPath(CustomOptionVo.getUlRootDirTemp());
  }

  /**
   * 输入流转成文件
   *
   * @param is             输入流
   * @param entireFileName 要保存的文件的完整文件名，包括物理路径
   */
  public static void transInputStream2File(InputStream is, String entireFileName) {
    File file = new File(entireFileName);
    int defaultSize = 1024;
    try (is; OutputStream os = new FileOutputStream(file)) {
      int len;
      byte[] buffer = new byte[1024];
      while ((len = is.read(buffer, 0, defaultSize)) != -1) {
        os.write(buffer, 0, len);
      }
      os.close();
      os.flush();
    } catch (IOException ex) {
      throw new CustomException(ResponseCodeEnum.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * 根据包含物理路径的指定路径名创建完整路径
   * java中没有Directory这个类的
   * JAVA设计这个的时候的想法和LINUX类似，所有的都是文件
   * 文件夹其实也是一种特殊的文件
   */
  public static void createDirectory(String entirePath) {
    // 根据系统的实际情况选择目录分隔符（windows下是，linux下是/）
    // String separator = File.separator;
    // String directory = "myDir1" + separator + "myDir2";
    // 以下这句的效果等同于上面两句，windows下正斜杠/和反斜杠\都是可以的
    // linux下只认正斜杠，为了保证跨平台性，不建议使用反斜杠
    // 在java程序中是转义字符，用\来表示反斜杠
    // String directory = "myDir1/myDir2";
    // 准备一个File对象，利用File对象创建目录
    File file = new File(entirePath);
    // 如果这个目录已经存在则直接返回
    if (file.exists()) {
      return;
    }
    // 如果不存在，则创建此目录
    if (!file.mkdirs()) {
      throw new CustomException(ResponseCodeEnum.ERROR_DIRECTORY_CREATE);
    }
  }

  /**
   * 获取上传文件的文件名，不包括扩展名
   */
  public static String getFileName(String entireFileName) {
    if (!StringUtils.hasLength(entireFileName)) {
      return "";
    }
    int dotPosition = entireFileName.lastIndexOf(".");
    if (dotPosition == -1) {
      // dotPosition == -1 说明文件名中没有"."
      return entireFileName;
    } else {
      return entireFileName.substring(0, dotPosition);
    }
  }

  /**
   * 获取上传文件的扩展名，扩展名前有“.”
   */
  public static String getExtName(String entireFileName) {
    if (!StringUtils.hasLength(entireFileName)) {
      return "";
    }
    int dotPosition = entireFileName.lastIndexOf(".");
    if (dotPosition == -1) {
      return "";
    } else {
      return entireFileName.substring(dotPosition);
    }
  }

  /**
   * 根据扩展名判断上传的文件是否是图像文件
   */
  public static boolean isImageByExt(String extName, UploadConfigVo configDto) {
    String[] allAllowedExtImg = configDto.getAllowedExtImg().split(",");
    for (String allowedExtImg : allAllowedExtImg) {
      if (extName.equals(allowedExtImg)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 根据扩展名判断上传的文件是否是图像文件
   */
  public static boolean isImageByLoad(String entireServerFileName) {
    File file = new File(entireServerFileName);
    if (!file.exists()) {
      return false;
    }
    try {
      return Objects.nonNull(ImageIO.read(file));
    } catch (IOException e) {
      return false;
    }
  }

  /**
   * 根据流判断上传的文件是否是图像文件，这个方法更靠谱
   * aaa.doc 改成 aaa.jpg 用流读出来的是null
   */
  public static BufferedImage getImage(String entireServerFileName) {
    if (!isImageByLoad(entireServerFileName)) {
      return null;
    }
    try {
      return ImageIO.read(new File(entireServerFileName));
    } catch (IOException e) {
      return null;
    }
  }

  /**
   * 修改文件名
   *
   * @param entireFileName 要修改文件名的文件的完整文件名，包括物理路径
   * @param newFileName    newFileName 新的文件名，不包括路径和扩展名
   */
  public static Boolean modifyFileName(String entireFileName, String newFileName) {
    File file = new File(entireFileName);
    if (!file.exists()) {
      throw new CustomException(ResponseCodeEnum.ERROR_FILE_NOT_FOUND);
    }
    String path = file.getParent();
    String extName = getExtName(entireFileName);
    newFileName = newFileName + extName;
    File fileNew = new File(path + SEPARATOR + newFileName);
    try {
      return file.renameTo(fileNew);
    } catch (SecurityException ex) {
      throw new CustomException(ResponseCodeEnum.ERROR_FILE_RENAME);
    }
  }

  /**
   * 将一个文件拷贝到另外一个文件（流方式）
   *
   * @param source 要拷贝的文件，包括完整的绝对路径及文件名及扩展名
   * @param target 目标文件，包括完整的绝对路径及文件名及扩展名
   */
  public static boolean copyByStream(String source, String target) {
    // 先验证源文件是否存在
    File fileSource = new File(source);
    if (!fileSource.exists()) {
      throw new CustomException(ResponseCodeEnum.ERROR_FILE_NOT_FOUND);
    }
    try (InputStream is = new FileInputStream(source);
         OutputStream os = new FileOutputStream(target)) {
      byte[] buffer = new byte[1024];
      int length;
      while ((length = is.read(buffer)) > 0) {
        os.write(buffer, 0, length);
      }
    } catch (IOException e) {
      throw new CustomException(ResponseCodeEnum.ERROR_FILE_EXISTED);
      // return false;
    }
    return true;
  }

  /**
   * 删除物理硬盘上的真实文件
   */
  public static void delRhysicsFile(String entireFileName) {
    File file = new File(entireFileName);
    if (!file.exists()) {
      return;
    }
    // 执行到这里说明文件存在，尝试进行删除操作
    try {
      Files.delete(Paths.get(entireFileName));
    } catch (IOException e) {
      throw new CustomException(ResponseCodeEnum.ERROR_FILE_DELETE);
    }
  }

  /**
   * 从系统的yml配置文件中读取根目录配置，如果目录不存在就创建它，注意最后以“/”结尾
   *
   * @return 临时目录根目录路径字符串
   */
  private static String gainUlDlPhysicsRootPath(String rootPathString) {
    if (!StringUtils.hasLength(rootPathString)) {
      throw new CustomException(ResponseCodeEnum.INTERNAL_SERVER_ERROR);
    }
    Path realPhysicsPath = Paths.get(rootPathString).toAbsolutePath().normalize();
    // 从配置文件中获取了目录名，创建这个目录，确保此目录一定存在
    try {
      Files.createDirectories(realPhysicsPath);
    } catch (Exception ex) {
      throw new CustomException(ResponseCodeEnum.INTERNAL_SERVER_ERROR);
    }
    String result = realPhysicsPath.toString();
    if (!result.endsWith(SEPARATOR)) {
      result += SEPARATOR;
    }
    return result;
  }

}
